/*
 * DifferentShaderConstructors.cpp
 *
 *  Created on: 12.05.2012
 *      Author: Kirill Blinov
 *      Copyright: LGPL
 */

#include "IShaderParamParser.h"
#include "../BasicTypes.h"
#include "../Constants.h"
#include <boost/lexical_cast.hpp>


bool
CParseCommonInputValues::TryToApplyParameter( const size_t VariablePosition,
                                  	  	  	  const size_t ValuePosition,
                                  	  	  	  const std::string TokenString,
                                  	  	  	  IShaderProgram *ShaderProgram,
                                  	  	  	  const std::vector< std::string > SortedShaderTextures,
                                  	  	  	  bool *Result ) const
{
	std::string ParameterType = TokenString.substr( 0, VariablePosition - 1 );
	bool LocalResult = true;

	bool ShaderTypeEqual = ( ParameterType.compare( TO_STRING( SHADER_INPUT_COMMON_VALUES ) ) == 0 );
	if ( ShaderTypeEqual )
	{
		SHADER_INPUT_COMMON_VALUES ShaderInputValue;
		std::string Parameter = TokenString.substr( VariablePosition, ValuePosition - VariablePosition - 1 );
		std::string Value = TokenString.substr( ValuePosition );

		if ( Value.compare( TO_STRING( SHADER_INPUT_MVP_MATRIX ) ) == 0 )
		{
			ShaderInputValue = SHADER_INPUT_MVP_MATRIX;
		}
		else if ( Value.compare( TO_STRING( SHADER_INPUT_MODEL_MATRIX ) ) == 0 )
		{
			ShaderInputValue = SHADER_INPUT_MODEL_MATRIX;
		}
		else if ( Value.compare( TO_STRING( SHADER_INPUT_BONES_ARRAY_32 ) ) == 0 )
		{
			ShaderInputValue = SHADER_INPUT_BONES_ARRAY_32; //TODO In this case we have to get access to uniform another way
		}
		else if ( Value.compare( TO_STRING( SHADER_INPUT_LIGHT_DIRECTION ) ) == 0 )
		{
			ShaderInputValue = SHADER_INPUT_LIGHT_DIRECTION;
		}
		else if ( Value.compare( TO_STRING( SHADER_INPUT_AMBLIENT_COLOR ) ) == 0 )
		{
			ShaderInputValue = SHADER_INPUT_AMBLIENT_COLOR;
		}
		else if ( Value.compare( TO_STRING( SHADER_INPUT_CAMERA_DISTANCE ) ) == 0 )
		{
			ShaderInputValue = SHADER_INPUT_CAMERA_DISTANCE;
		}
		else if ( Value.compare( TO_STRING( SHADER_INPUT_DIFFUSE_MAP ) ) == 0 )
		{
			UINT TextureSlotCounter = 0;
			for ( std::vector< std::string >::const_iterator Begin = SortedShaderTextures.begin(), End = SortedShaderTextures.end();
				  Begin != End; Begin++ )
			{
				if ( Parameter.compare( *Begin ) == 0 )
				{
					break;
				}
				TextureSlotCounter++;
			}
			Parameter.assign( SHADER_PARAMETER_TEXTURE_PREFIX );
			Parameter += boost::lexical_cast<std::string>( TextureSlotCounter );

			ShaderInputValue = SHADER_INPUT_DIFFUSE_MAP;
		}
		else
		{
			//Crash!
			assert( false );
		}

		CVisualisator* Visualisator = CVisualisator::GetVisualisator();

		LocalResult = Visualisator->DefineShaderInputParam<SHADER_INPUT_COMMON_VALUES>( ShaderProgram,
																						Parameter,
																						ShaderInputValue );
	}

	if ( Result != NULL )
	{
		*Result = LocalResult;
	}
	return ShaderTypeEqual;
}

bool
CParseNativeInputTypes::TryToApplyParameter( const size_t VariablePosition,
                                  	  	  	 const size_t ValuePosition,
                                  	  	  	 const std::string TokenString,
                                  	  	  	 IShaderProgram *ShaderProgram,
                                  	  	  	 const std::vector< std::string > SortedShaderTextures,
                                  	  	  	 bool *Result ) const
{
	std::string ParameterType = TokenString.substr( 0, VariablePosition - 1 );
	std::string Parameter = TokenString.substr( VariablePosition, ValuePosition - VariablePosition - 1 );
	std::string ParameterValue = TokenString.substr( ValuePosition );

	CVisualisator* Visualisator = CVisualisator::GetVisualisator();
	bool LocalResult = true;
	bool TypeFound = false;

	if ( ParameterType.compare( TO_STRING( SHADER_INPUT_UINT ) ) == 0 )
	{
		TypeFound = true;
		UINT Value = static_cast<UINT>( atoi( ParameterValue.c_str() ) );
		LocalResult = Visualisator->DefineShaderInputParam<UINT>( ShaderProgram,
				                                             	  Parameter,
				                                             	  Value );
	}
	else if ( ParameterType.compare( TO_STRING( SHADER_INPUT_INT ) ) == 0 )
	{
		TypeFound = true;
		int Value = atoi( ParameterValue.c_str() );
		LocalResult = Visualisator->DefineShaderInputParam<int>( ShaderProgram,
				                                            	 Parameter,
				                                            	 Value );
	}
	else if ( ParameterType.compare( TO_STRING( SHADER_INPUT_FLOAT ) ) == 0 )
	{
		TypeFound = true;
		float Value = atof( ParameterValue.c_str() );
		LocalResult = Visualisator->DefineShaderInputParam<float>( ShaderProgram,
				                                              	   Parameter,
				                                              	   Value );
	}
	else if ( ParameterType.compare( TO_STRING( SHADER_INPUT_VEC3 ) ) == 0 )
	{
		TypeFound = true;
		char *NextPtr = NULL;
		float FirstValue = strtod( ParameterValue.c_str(), &NextPtr );
		float SecondValue = strtod( NextPtr, &NextPtr );
		float ThirdValue = strtod( NextPtr, &NextPtr );

		glm::vec3 Value();
		LocalResult = Visualisator->DefineShaderInputParam<glm::vec3>( ShaderProgram,
				                                                  	   Parameter,
				                                                  	   glm::vec3( FirstValue,
				                                                  			   	  SecondValue,
				                                                  			   	  ThirdValue ) );
	}
	else if ( ParameterType.compare( TO_STRING( SHADER_INPUT_VEC4 ) ) == 0 )
	{
		TypeFound = true;
		char *NextPtr = NULL;
		float FirstValue = strtod( ParameterValue.c_str(), &NextPtr );
		float SecondValue = strtod( NextPtr, &NextPtr );
		float ThirdValue = strtod( NextPtr, &NextPtr );
		float FourthValue = strtod( NextPtr, &NextPtr );

		LocalResult = Visualisator->DefineShaderInputParam<glm::vec4>( ShaderProgram,
				                                                  	   Parameter,
				                                                  	   glm::vec4( FirstValue,
				                                                  			   	  SecondValue,
				                                                  			   	  ThirdValue,
				                                                  			   	  FourthValue ) );
	}
	else if ( ParameterType.compare( TO_STRING( SHADER_INPUT_MAT4 ) ) == 0 )
	{
		TypeFound = true;
		char *NextPtr = NULL;
		glm::vec4 TempVectors[4];

		for ( int i = 0; i < 4; i++ )
		{
			float FirstValue = strtod( ParameterValue.c_str(), &NextPtr );
			float SecondValue = strtod( NextPtr, &NextPtr );
			float ThirdValue = strtod( NextPtr, &NextPtr );
			float FourthValue = strtod( NextPtr, &NextPtr );

			TempVectors[i] = glm::vec4( FirstValue, SecondValue, ThirdValue, FourthValue );
		}


		LocalResult = Visualisator->DefineShaderInputParam<glm::mat4>( ShaderProgram,
																	   Parameter,
																	   glm::mat4( TempVectors[0],
																				  TempVectors[1],
																				  TempVectors[2],
																				  TempVectors[3] ) );
	}

	if ( Result != NULL )
	{
		*Result = LocalResult;
	}
	return TypeFound;
}

bool
CParseSinCallbac::TryToApplyParameter( const size_t VariablePosition,
                                  	   const size_t ValuePosition,
                                  	   const std::string TokenString,
                                  	   IShaderProgram *ShaderProgram,
                                  	   const std::vector< std::string > SortedShaderTextures,
                                  	   bool *Result ) const
{
	std::string ParameterType = TokenString.substr( 0, VariablePosition - 1 );

	bool TypeIsMatch = ( ParameterType.compare( TO_STRING( SHADER_INPUT_SINUS_CALLBACK ) ) == 0 );
	bool LocalResult = true;

	if ( TypeIsMatch )
	{
		std::string Parameter = TokenString.substr( VariablePosition, ValuePosition - VariablePosition - 1 );
		std::string ParameterValue = TokenString.substr( ValuePosition );
		CVisualisator* Visualisator = CVisualisator::GetVisualisator();

		float CallbackInput = atof( ParameterValue.c_str() );
		boost::shared_ptr<float> Accumulator( new float );
		*Accumulator = 0.0f;
		LocalResult = Visualisator->DefineShaderInputParam<float>( ShaderProgram,
															  	   Parameter,
															  	   boost::bind( &CParseSinCallbac::Callback,
				                                                           	    this,
				                                                           	    CallbackInput,
				                                                           	    Accumulator ) );
	}

	if ( Result != NULL )
	{
		*Result = LocalResult;
	}
	return TypeIsMatch;
}

float
CParseSinCallbac::Callback( float Step, boost::shared_ptr<float> Accumulator ) const
{
	*Accumulator = ( *Accumulator < (1.0 - Step) ) ? ( *Accumulator + Step ) : 0;
	return *Accumulator;
}
